package ute.mvc.controller.portal.student;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import ute.mvc.dto.NextData;
import ute.mvc.dto.QuestionDTO;
import ute.mvc.dto.SubmittedTestDTO;
import ute.mvc.dto.TestDTO;
import ute.mvc.model.MyClass;
import ute.mvc.model.OpenCourse;
import ute.mvc.model.OpenCourse_Subject;
import ute.mvc.model.SubmittedTestAndQuizz;
import ute.mvc.model.TestAndQuizz;
import ute.mvc.model.TestAndQuizz_Student;
import ute.mvc.model.User;
import ute.mvc.service.ClassService;
import ute.mvc.service.Course_SubjectService;
import ute.mvc.service.OpenCourseService;
import ute.mvc.service.SubmittedTestAndQuizzService;
import ute.mvc.service.TestAndQuizzService;
import ute.mvc.service.TestAndQuizz_StudentService;
import ute.mvc.service.UserService;
import ute.mvc.util.JsonObject;
import ute.mvc.util.TestUtil;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Controller
@RequestMapping(value = { "/student/{studentId}/{openCourseId}/{subjectId}/test" })
public class StudentTestController {

	@Autowired
	UserService userService;
	@Autowired
	OpenCourseService openCourseService;
	@Autowired
	ClassService classService;
	@Autowired
	Course_SubjectService courseSubjectService;
	@Autowired
	TestAndQuizzService testService;
	@Autowired
	TestAndQuizz_StudentService testAndQuizz_StudentService;
	@Autowired
	SubmittedTestAndQuizzService submittedService;

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String getTestList(@PathVariable String openCourseId,
			@PathVariable String subjectId, Model model) {

		System.out.println("List Test: ");
		return "student_test_list";
	}

	@RequestMapping(value = { "/{testId}/", "/{testId}/submitted" }, method = RequestMethod.GET)
	public String getTestDetail(@PathVariable int studentId,
			@PathVariable String testId, @PathVariable String openCourseId,
			@PathVariable String subjectId, Model model) {
		// Show Begin Test
		boolean flagTest = true;

		TestAndQuizz test = testService.getTestAndQuizz(testId);
		TestAndQuizz_Student testAndQuizzStudent = testAndQuizz_StudentService
				.getTestAndQuizz_Student(testId + "_" + studentId);
		if (testAndQuizzStudent != null
				&& testAndQuizzStudent.getListSubmittedTestAndQuizz().size() >= testAndQuizzStudent
						.getTestAndQuizz().getSubmitTimeAllow()) {
			flagTest = false;
			System.out.println("Over submit");
		}

		if (test.getEndDay().before(new Date())) {
			flagTest = false;
			System.out.println("Time over");
		}
		model.addAttribute("test", test);
		model.addAttribute("flagTest", flagTest);
		System.out.println("List Test Detail: ");
		return "student_test_detail";
	}

	@RequestMapping(value = "/feedback/{submittedTestId}", method = RequestMethod.GET)
	public String getTestFeedBack(@PathVariable String submittedTestId,
			@PathVariable String openCourseId, @PathVariable String subjectId,
			Model model) {

		SubmittedTestAndQuizz submitted = submittedService
				.getSubmittedTestAndQuizz(submittedTestId);
		
		if(submitted == null)
			return "redirect:../";
		
		String feedBack = submitted.getExamQuestion();
		// Parse feedback to TestAndQuizz Object
		ObjectMapper map = new ObjectMapper();
		// Add to test model and send to feedback
		TestDTO testDTO = null;
		try {
			testDTO = map.readValue(feedBack, TestDTO.class);
		} catch (JsonParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JsonMappingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		TestUtil.formatQuestionDTO(testDTO);
		model.addAttribute("test", testDTO);
		// Get Question in TestPart
		return "student_test_feedback";
	}

	@RequestMapping(value = "/{testId}/test", method = RequestMethod.GET)
	public String getTest(@PathVariable int studentId,
			@PathVariable String testId, @PathVariable String openCourseId,
			@PathVariable String subjectId, Model model) {

		// Begin Test
		// If current in test time, return the test in process
		TestAndQuizz test = testService.getTestAndQuizz(testId);
		if (test.getEndDay().before(new Date())) {
			// Out of time
			return "redirect:";
		}

		TestAndQuizz_Student testAndQuizzStudent = testAndQuizz_StudentService
				.getTestAndQuizz_Student(testId + "_" + studentId);
		if (testAndQuizzStudent == null) {
			// Create new Test_Student for first Test
			testAndQuizzStudent = new TestAndQuizz_Student();
			testAndQuizzStudent.setTestAndQuizz_StudentId(testId + "_"
					+ studentId);
			User student = userService.getUserById(studentId);
			testAndQuizzStudent.setStudent(student);
			testAndQuizzStudent.setTestAndQuizz(test);
			testAndQuizz_StudentService
					.addTestAndQuizz_Student(testAndQuizzStudent);
		}

		// Get number of submitted TestAndQuizz
		int testNumber = 0;
		int questionNumber = -1;
		SubmittedTestAndQuizz submittedTest = null;

		testNumber = testAndQuizzStudent.getListSubmittedTestAndQuizz().size();

		// Check last test to get question number
		questionNumber = submittedService
				.getInCompleteSubmittedTestAndQuizz(testId + "_" + studentId
						+ "_" + testNumber);
		
		System.out.println(questionNumber);

		// If has incomplete test
		if (questionNumber > -1) {

			submittedTest = submittedService.getSubmittedTestAndQuizz(testId
					+ "_" + studentId + "_" + testNumber);
			int seconds = (int) ((new Date().getTime() - submittedTest
					.getStartTime().getTime()) / 1000);

			if (seconds >= test.getTestDuration() * 60
					|| submittedTest.getSubmittedTime() != null) {
				// try to create new test
				// Can not create new SubmittedTest
				if (testNumber >= testAndQuizzStudent.getTestAndQuizz()
						.getSubmitTimeAllow()) {

					return "redirect:";
				}
				// Create new if all Submitted test is completed
				submittedTest = new SubmittedTestAndQuizz();
				submittedTest.setTestAndQuizz_student(testAndQuizzStudent);
				submittedTest.setSubmittedTestAndQuizzId(testAndQuizzStudent
						.getTestAndQuizz_StudentId() + "_" + (testNumber + 1));
				submittedTest.setStartTime(new Date());

				// Get Test for each Student
				TestDTO testDTO = testService.getTestDTO(test);

				ObjectMapper map = new ObjectMapper();
				String json = "";
				try {
					json = map.writeValueAsString(testDTO);
				} catch (JsonProcessingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				// System.out.println(json);
				// Create Question
				submittedTest.setExamQuestion(json);
				submittedService.addSubmittedTestAndQuizz(submittedTest);
				model.addAttribute("submitted", submittedTest);

				// add to client in seconds
				model.addAttribute("duration", test.getTestDuration() * 60);
				model.addAttribute("questionNumber", 0);
			} else {

				// Send to client
				model.addAttribute("submitted", submittedTest);
				// Recaculate test duration, return int
				model.addAttribute("duration", test.getTestDuration() * 60
						- seconds);
				model.addAttribute("questionNumber", questionNumber);
			}

		}
		// if not
		else {
			// Can not create new SubmittedTest
			if (testNumber >= testAndQuizzStudent.getTestAndQuizz()
					.getSubmitTimeAllow()) {
				return "redirect:";
			}
			// Create new if all Submitted test is completed
			submittedTest = new SubmittedTestAndQuizz();
			submittedTest.setTestAndQuizz_student(testAndQuizzStudent);
			submittedTest.setSubmittedTestAndQuizzId(testAndQuizzStudent
					.getTestAndQuizz_StudentId() + "_" + (testNumber + 1));
			submittedTest.setStartTime(new Date());

			// Get Test for each Student
			TestDTO testDTO = testService.getTestDTO(test);
			ObjectMapper map = new ObjectMapper();
			String json = "";
			try {
				json = map.writeValueAsString(testDTO);
			} catch (JsonProcessingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// System.out.println(json);
			// Create Question
			submittedTest.setExamQuestion(json);
			submittedService.addSubmittedTestAndQuizz(submittedTest);
			model.addAttribute("submitted", submittedTest);

			// add to client in seconds
			model.addAttribute("duration", test.getTestDuration() * 60);
			model.addAttribute("questionNumber", 0);
		}
		
		//load total question
		int numberQuestion = submittedService.getNumberQuestion(testId
					+ "_" + studentId + "_" + testNumber);
		model.addAttribute("numberQuestion", numberQuestion);
		return "student_test_process";
	}

	@RequestMapping(value = "/{testId}/next", method = RequestMethod.POST)
	@ResponseBody
	public QuestionDTO getNextQuestion(@RequestBody NextData data) {

		SubmittedTestAndQuizz submitted = submittedService
				.getSubmittedTestAndQuizz(data.getTestId());

		// Duration in minutes
		int duration = submitted.getTestAndQuizz_student().getTestAndQuizz()
				.getTestDuration();
		// In miliseconds
		long acceptTime = submitted.getStartTime().getTime() + duration * 60
				* 1000;
		long getTime = new Date().getTime();
		// Out of time
		// Delay 5s for transmission
		if (getTime > acceptTime + 5000) {
			System.out.println("Error in get next question: " + getTime + " vs " + acceptTime);
			QuestionDTO dto = new QuestionDTO();
			dto.setQuestionId("0000");;
			return dto;
		}

		String feedBack = submitted.getExamQuestion();
		// Parse feedback to TestAndQuizz Object
		ObjectMapper map = new ObjectMapper();
		// Add to test model and send to feedback
		TestDTO testDTO = null;
		try {
			testDTO = map.readValue(feedBack, TestDTO.class);
		} catch (JsonParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JsonMappingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// Update answer of student click next
		if (!"".equals(data.getQuestionId())) {

			String answer = submitted.getAnswerOfStudent();
			answer += data.getQuestionNumber() + "<>" + data.getQuestionId()
					+ "<>" + data.getAnswer() + ";";
			submitted.setAnswerOfStudent(answer);
			submittedService.updateSubmittedTestAndQuizz(submitted);
		}

		QuestionDTO dto = null;
		if (data.getQuestionNumber() == 0)
			dto = testDTO.getListPart().get(0).getListQuestion().get(0);
		else {
			dto = submittedService.getNextQuestion(data);
		}
		// Hide answer
		if (dto != null) {
			dto.setRightAnswer("");
		} else {
			dto = new QuestionDTO();
		}
		return dto;
	}

	@RequestMapping(value = "/{testId}/point", method = RequestMethod.POST)
	@ResponseBody
	public String getPoint(@RequestBody String id) {

		SubmittedTestAndQuizz submitted = submittedService
				.getSubmittedTestAndQuizz(id);

		// Duration in minutes
		int duration = submitted.getTestAndQuizz_student().getTestAndQuizz()
				.getTestDuration();

		// In miliseconds
		long acceptTime = submitted.getStartTime().getTime() + duration * 60
				* 1000;
		long submitTime = new Date().getTime();
		System.out.println(submitTime + " vs " + acceptTime);
		// Out of time
		// Delay 5s for transmission
		if (submitTime > acceptTime + 5000) {
			System.out.println("Error: " + submitTime + " vs " + acceptTime);
			return "ERROR";
		}

		submitted.setSubmittedTime(new Date());
		// Point?
		submittedService.pointTest(submitted);
		submittedService.updateSubmittedTestAndQuizz(submitted);
		return "" + submitted.getPoint();
	}

	/**
	 * Get All Test & Quizz for concrete Test
	 * 
	 * @param studentId
	 * @param openCourseId
	 * @param subjectId
	 * @param model
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@RequestMapping(value = "/{testId}/listJSON", method = RequestMethod.GET)
	@ResponseBody
	public JsonObject getTestListDetailJson(@PathVariable String testId,
			@PathVariable int studentId, @PathVariable String openCourseId,
			@PathVariable String subjectId, Model model) {

		User student = userService.getUserById(studentId);
		// Has not start test => null in testAndQuizzStudent
		TestAndQuizz_Student testAndQuizzStudent = testService
				.getTestAndQuizzByStudent(student, testId);
		List<SubmittedTestDTO> lstSubmittedTest = new ArrayList<SubmittedTestDTO>();

		// hasn't start test ==> empty SubmittedTest
		if (testAndQuizzStudent != null) {
			for (SubmittedTestAndQuizz s : testAndQuizzStudent
					.getListSubmittedTestAndQuizz()) {
				lstSubmittedTest.add(new SubmittedTestDTO(s));
			}
		}

		JsonObject jsonObject = new JsonObject();
		jsonObject.setAaData(lstSubmittedTest);
		jsonObject.setsEcho(1);
		jsonObject.setiTotalDisplayRecords(lstSubmittedTest.size());
		jsonObject.setiTotalRecords(lstSubmittedTest.size());
		return jsonObject;
	}

	/**
	 * List All Test Assigned For Student
	 * 
	 * @param studentId
	 * @param openCourseId
	 * @param subjectId
	 * @param model
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@RequestMapping(value = "/listJSON", method = RequestMethod.GET)
	@ResponseBody
	public JsonObject getTestListJson(@PathVariable int studentId,
			@PathVariable String openCourseId, @PathVariable String subjectId,
			Model model) {

		User student = userService.getUserById(studentId);
		OpenCourse oCourse = openCourseService.getOpenCourseById(openCourseId);
		MyClass uClass = openCourseService.getClass(student, oCourse);
		OpenCourse_Subject oCourseSubject = courseSubjectService
				.getCourse_Subject(openCourseId + "_" + subjectId);
		List<TestAndQuizz> lstTest = classService
				.getAllTestInOpenCourseSubject(oCourseSubject,
						uClass.getClassId());
		System.out.println(uClass.getClassName());
		System.out.println("List Test: " + lstTest.size());
		JsonObject jsonObject = new JsonObject();
		jsonObject.setAaData(lstTest);
		jsonObject.setsEcho(1);
		jsonObject.setiTotalDisplayRecords(lstTest.size());
		jsonObject.setiTotalRecords(lstTest.size());
		return jsonObject;
	}
}
